#include "session.h"
#include "message_node.hpp"
#include "net_global.h"
#include "server.h"
#include "logic.h"
#include "singleton.hpp"

#include <boost/asio/detail/socket_ops.hpp>
#include <iostream>

#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <mutex>
#include <unistd.h>


Session::Session(IOContext& io_context, Server* server)
:_io_context(io_context)
,_socket(io_context)
,_server(server)
,_is_closing(false)
,_head_node(new MsgNode(HEAD_TOTAL_LEN))
{
    boost::uuids::uuid uuid = boost::uuids::random_generator()();
    _uuid = boost::uuids::to_string(uuid);
}

Session::~Session()
{
    close();
}

void Session::start()
{
    auto self = shared_from_this();

    //启动协程，等待客户端发送数据
    boost::asio::co_spawn(_io_context, [self]() -> boost::asio::awaitable<void> {
        try 
        {
            while(!self->_is_closing)
            {
                self->_head_node->Clear();
                auto n = co_await boost::asio::async_read(self->_socket, boost::asio::buffer(self->_head_node->_data, HEAD_TOTAL_LEN), boost::asio::use_awaitable);
                if(n == 0)
                {
                    self->close();
                    self->_server->delete_session(self->uuid());
                    std::cerr << "read head error: " << n << std::endl;
                    co_return;
                }
                
                uint len = 0;
                uint id = 0;
                memcpy(&id, self->_head_node->_data, HEAD_MSG_ID);
                memcpy(&len, self->_head_node->_data + HEAD_MSG_ID, HEAD_MSG_LEN);
                id = boost::asio::detail::socket_ops::network_to_host_short(id);
                len = boost::asio::detail::socket_ops::network_to_host_short(len);
                std::cout << "id: " << id << " len: " << len << std::endl;

                if(len > MAX_LENGTH || id > MAX_ID_SIZE)
                {
                    self->close();
                    self->_server->delete_session(self->uuid());
                    std::cerr << "len: " << len << " id: " << id << std::endl;
                    co_return;
                }
                
                self->_recv_node = std::make_shared<RecvNode>(len, id);
                n = co_await boost::asio::async_read(self->_socket, boost::asio::buffer(self->_recv_node->_data, len), boost::asio::use_awaitable);
                if(n == 0)
                {
                    self->close();
                    self->_server->delete_session(self->uuid());
                    std::cerr << "read body error: " << n << std::endl;
                    co_return;
                }
                self->_recv_node->_data[self->_recv_node->_total_len] = '\0';
                std::cout << "recv: " << self->_recv_node->_data << std::endl;
                
                Singleton<Logic>::GetInstance()->post_task(std::make_shared<Task>(self, self->_recv_node));
            }
        }
        catch (const std::exception& e) {
            self->close();
            self->_server->delete_session(self->uuid());
            std::cout << "Exception in Session: " << e.what() << '\n';
        }
    }, boost::asio::detached);
}


void Session::close()
{
    _is_closing = true;
    _socket.close();
}

//发送时并没有使用协程
void Session::send(const char* data, size_t len, uint16_t id)
{
    std::unique_lock<std::mutex> lock(_send_mutex);
    auto size = _send_queue.size();
    if(size > MAX_SENDOUE)
    {
        std::cerr << "send queue is full" << std::endl;
        return;
    }

    _send_queue.push(std::make_shared<SendNode>(data, len, id));
    if(size > 0)
    {
        return;
    }

    auto node = _send_queue.front();
    lock.unlock();

    auto self = shared_from_this();
    boost::asio::async_write(_socket, boost::asio::buffer(node->_data, node->_total_len), 
        [this, self](const boost::system::error_code& error, size_t bytes_transferred){
            handle_wirte(error, bytes_transferred, self);
    });
}

void Session::handle_wirte(const boost::system::error_code& error, size_t bytes_transferred, Session::ptr self)
{
    if(error)
    {
        self->close();
        self->_server->delete_session(self->uuid());
        std::cerr << "write error: " << error.message() << std::endl;
        return;
    }

    std::unique_lock<std::mutex> lock(_send_mutex);
    _send_queue.pop();
    if(_send_queue.empty())
    {
        return;
    }

    auto node = _send_queue.front();
    lock.unlock();
    boost::asio::async_write(_socket, boost::asio::buffer(node->_data, node->_total_len), 
        [this, self](const boost::system::error_code& error, size_t bytes_transferred){
            handle_wirte(error, bytes_transferred, self);
    });
}